﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Shell.Interop;
using System.IO;
using Microsoft.VisualStudio;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Xml;

using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants;
using System.Drawing;
using Microsoft.VisualStudio.Shell;

namespace PovTeam.Pkg_PovrayProject
{
    [GuidAttribute("b5385103-5d72-4185-8f9d-5b673cacb073")]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    public class ProjectBrowseItem
    {
        public string Name { get { return name; } }
        internal string name;
    }

    class PovrayProjectNode : IVsProject3, IVsHierarchy, IPersistFileFormat, IVsUIHierarchy, Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget
    {
        static bool bLogProperties = false;
        static bool bLogRootProperties = false;
        static bool bLogQueryComands = false;
        static bool bLogExecCommands = true;
        static bool bLogFunctions = false;

        private PovrayProjectFactory factory;
        private Microsoft.VisualStudio.OLE.Interop.IServiceProvider site;
        private ImageList icons = new ImageList();

        private bool dirty;

        public PovrayProjectNode(PovrayProjectFactory factory)
        {
            this.factory = factory;

            dirty = true;

            icons.Images.Add(Resources.POV_Ray_Project);
            icons.Images.Add(Resources.POV_Ray_Scene);
            icons.Images.Add(Resources.POV_Ray_Include);

        }
        #region IVsProject Members

        uint new_item_id = 1;



        class ProjectItem
        {
            public string Moniker;
            public PovrayProjectNode node;
            public Dictionary<int, object> Properties = new Dictionary<int, object>();
        }

        private Dictionary<uint, ProjectItem> items = new Dictionary<uint, ProjectItem>();
        private Dictionary<string, uint> moniker_map = new Dictionary<string, uint>();

        public void CloneFile(string fileName, string newLocation, string newFileName)
        {
            LogFunctionCall("CloneFile", "fileName", fileName, "newLocation", newLocation, "newFileName", newFileName);
            string projectFile = Path.Combine(newLocation, newFileName);
            if (!Directory.Exists(newLocation))
            {
                Directory.CreateDirectory(newLocation);
            }
            File.Copy(fileName, projectFile);
            AddItem(new_item_id++, VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE, projectFile, 0, null, IntPtr.Zero, new VSADDRESULT[1]);
        }

        public void CloneFile(string fileName, string newLocation, string newFileName, VSConstants.VSITEMID itemID)
        {
            LogFunctionCall("CloneFile", "fileName", fileName, "newLocation", newLocation, "newFileName", newFileName, "itemID", itemID);
            string projectFile = Path.Combine(newLocation, newFileName);
            if (!Directory.Exists(newLocation))
            {
                Directory.CreateDirectory(newLocation);
            }
            File.Copy(fileName, projectFile);
            AddItem((uint)itemID, VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE, projectFile, 0, null, IntPtr.Zero, new VSADDRESULT[1]);
        }

        public int AddItem(uint itemidLoc, VSADDITEMOPERATION dwAddItemOperation, string pszItemName, uint cFilesToOpen, string[] rgpszFilesToOpen, IntPtr hwndDlgOwner, VSADDRESULT[] pResult)
        {
            LogFunctionCall("AddItem", "itemidLoc", itemidLoc, "dwAddItemOperation", dwAddItemOperation, "pszItemName", pszItemName, "cFilesToOpen", cFilesToOpen, "rgpszFilesToOpen", rgpszFilesToOpen, "hwndDlgOwner", hwndDlgOwner, "pResult", pResult);

            items.Add(itemidLoc, new ProjectItem());
            items[itemidLoc].Moniker = pszItemName;
            if ((VSConstants.VSITEMID)itemidLoc == VSConstants.VSITEMID.Root)
            {


            }
            else
            {

                items[itemidLoc].node = new PovrayProjectNode(factory);
                if (itemidLoc == 1) // this is the first child, the root needs to be updated with this.
                {
                    items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_FirstChild] = 1;
                    items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_FirstVisibleChild] = 1;
                }
            }
            moniker_map[pszItemName] = itemidLoc;
            pResult[0] = VSADDRESULT.ADDRESULT_Success;
            return VSConstants.S_OK;
        }

        public int GenerateUniqueItemName(uint itemidLoc, string pszExt, string pszSuggestedRoot, out string pbstrItemName)
        {
            LogFunctionCall("GenerateUniqueItemName", "itemidLoc", (VSConstants.VSITEMID)itemidLoc, "pszExt", pszExt, "pszSuggestedRoot", pszSuggestedRoot);
            throw new NotImplementedException();
        }

        public int GetItemContext(uint itemid, out Microsoft.VisualStudio.OLE.Interop.IServiceProvider ppSP)
        {
            LogFunctionCall("GetItemContext", (VSConstants.VSITEMID)itemid);
            ppSP = factory.GetOleServiceProvider();
            return VSConstants.S_OK;
        }

        public int GetMkDocument(uint itemid, out string pbstrMkDocument)
        {
            LogFunctionCall("GetMkDocument", (VSConstants.VSITEMID)itemid);
            if (items.ContainsKey(itemid))
            {
                pbstrMkDocument = items[itemid].Moniker;
                return VSConstants.S_OK;
            }
            else
            {
                pbstrMkDocument = null;
                return VSConstants.DISP_E_MEMBERNOTFOUND;
            }
        }

        public int IsDocumentInProject(string pszMkDocument, out int pfFound, VSDOCUMENTPRIORITY[] pdwPriority, out uint pitemid)
        {
            LogFunctionCall("IsDocumentInProject", "pszMkDocument", pszMkDocument);
            if (moniker_map.ContainsKey(pszMkDocument))
            {
                pfFound = 1;
                pdwPriority[0] = VSDOCUMENTPRIORITY.DP_Standard;
                pitemid = moniker_map[pszMkDocument];
                return VSConstants.S_OK;
            }
            else
            {
                pfFound = 0;
                pdwPriority[0] = VSDOCUMENTPRIORITY.DP_Unsupported;
                pitemid = 0;
                return VSConstants.DISP_E_MEMBERNOTFOUND;
            }
        }

        public int OpenItem(uint itemid, ref Guid rguidLogicalView, IntPtr punkDocDataExisting, out IVsWindowFrame ppWindowFrame)
        {
            LogFunctionCall("OpenItem", "itemid", itemid, "rguidLogicalView", rguidLogicalView, "punkDocDataExisting", punkDocDataExisting);
            throw new NotImplementedException();
        }

        #endregion

        #region IVsProject3 Members


        public int AddItemWithSpecific(uint itemidLoc, VSADDITEMOPERATION dwAddItemOperation, string pszItemName, uint cFilesToOpen, string[] rgpszFilesToOpen, IntPtr hwndDlgOwner, uint grfEditorFlags, ref Guid rguidEditorType, string pszPhysicalView, ref Guid rguidLogicalView, VSADDRESULT[] pResult)
        {
            LogFunctionCall("AddItemWithSpecific", "itemidLoc", (VSConstants.VSITEMID)itemidLoc, "dwAddItemOperation", dwAddItemOperation, "pszItemName", pszItemName, "cFilesToOpen", cFilesToOpen, "rgpszFilesToOpen", rgpszFilesToOpen, "hwndDlgOwner", hwndDlgOwner, "grfEditorFlags", grfEditorFlags, "rguidEditorType", rguidEditorType, "pszPhysicalView", pszPhysicalView, "rguidLogicalView", rguidLogicalView, "pResult", pResult);
            throw new NotImplementedException();
        }

        public int OpenItemWithSpecific(uint itemid, uint grfEditorFlags, ref Guid rguidEditorType, string pszPhysicalView, ref Guid rguidLogicalView, IntPtr punkDocDataExisting, out IVsWindowFrame ppWindowFrame)
        {
            LogFunctionCall("OpenItemWithSpecific", "itemid", (VSConstants.VSITEMID)itemid, "grfEditorFlags", grfEditorFlags, "rguidEditorType", rguidEditorType, "pszPhysicalView", pszPhysicalView, "rguidLogicalView", rguidLogicalView, "punkDocDataExisting", punkDocDataExisting);
            throw new NotImplementedException();
        }

        public int RemoveItem(uint dwReserved, uint itemid, out int pfResult)
        {
            LogFunctionCall("RemoveItem", "dwReserved", dwReserved, "itemid", (VSConstants.VSITEMID)itemid);
            throw new NotImplementedException();
        }

        public int ReopenItem(uint itemid, ref Guid rguidEditorType, string pszPhysicalView, ref Guid rguidLogicalView, IntPtr punkDocDataExisting, out IVsWindowFrame ppWindowFrame)
        {
            LogFunctionCall("ReopenItem", "itemid", (VSConstants.VSITEMID)itemid, "rguidEditorType", rguidEditorType, "pszPhysicalView", pszPhysicalView, "rguidLogicalView", rguidLogicalView, "punkDocDataExisting", punkDocDataExisting);
            throw new NotImplementedException();
        }

        public int TransferItem(string pszMkDocumentOld, string pszMkDocumentNew, IVsWindowFrame punkWindowFrame)
        {
            LogFunctionCall("TransferItem", "pszMkDocumentOld", pszMkDocumentOld, "pszMkDocumentNew", pszMkDocumentNew, "punkWindowFrame", punkWindowFrame);
            throw new NotImplementedException();
        }

        #endregion

        #region IVsHierarchy Members

        private uint eventCookie = 1;
        IVsHierarchyEvents eventSink;

        public int AdviseHierarchyEvents(IVsHierarchyEvents pEventSink, out uint pdwCookie)
        {
            LogFunctionCall("AdviseHierarchyEvents", "pEventSink", pEventSink);
            eventSink = pEventSink;
            pdwCookie = eventCookie;
            return VSConstants.S_OK;
        }

        public int Close()
        {
            LogFunctionCall("Close");
            return VSConstants.S_OK;
        }

        public int GetCanonicalName(uint itemid, out string pbstrName)
        {
            LogFunctionCall("GetCanonicalName", "itemid", (VSConstants.VSITEMID)itemid);
            if (items.ContainsKey(itemid))
            {
                pbstrName = items[itemid].Moniker;
                return VSConstants.S_OK;
            }
            else
            {
                pbstrName = null;
                return VSConstants.DISP_E_MEMBERNOTFOUND;
            }

        }

        public int GetGuidProperty(uint itemid, int propid, out Guid pguid)
        {
            Object obj;
            int hr = GetProperty(itemid, propid, out obj);
            if (hr == VSConstants.S_OK)
            {
                pguid = (Guid)obj;
            }
            else
            {
                pguid = Guid.Empty;
            }

            return hr;
        }

        /// <summary>
        /// Logs a function call
        /// </summary>
        /// <param name="FunctionName">The name of the function</param>
        /// <param name="args">arguments in the following form: name,value,name,value ... name,value</param>
        void LogFunctionCall(string FunctionName, params object[] args)
        {
            if (!bLogFunctions) return;
            System.Diagnostics.Debug.Write(FunctionName);
            System.Diagnostics.Debug.Write("(");
            bool toggle = false;
            bool first = true;
            foreach (object arg in args)
            {

                if (toggle)
                {
                    System.Diagnostics.Debug.Write(arg);
                }
                else
                {
                    if (!first) System.Diagnostics.Debug.Write(", ");
                    first = false;
                    System.Diagnostics.Debug.Write(arg);
                    System.Diagnostics.Debug.Write("=");
                }
                toggle = !toggle;
            }
            System.Diagnostics.Debug.WriteLine(")");
        }

        public int GetNestedHierarchy(uint itemid, ref Guid iidHierarchyNested, out IntPtr ppHierarchyNested, out uint pitemidNested)
        {
            LogFunctionCall("GetNestedHierarchy", "itemid", (VSConstants.VSITEMID)itemid, "iidHierarchyNested", iidHierarchyNested);

            ppHierarchyNested = IntPtr.Zero;
            pitemidNested = 0;
            return VSConstants.E_FAIL;
            if (items.ContainsKey(itemid))
            {


                IntPtr pUnkHier = Marshal.GetIUnknownForObject(items[itemid].node);
                IntPtr pHier;
                if (VSConstants.S_OK == Marshal.QueryInterface(pUnkHier, ref iidHierarchyNested, out pHier))
                {
                    ppHierarchyNested = pHier;
                    pitemidNested = 1;
                    return VSConstants.S_OK;
                }
                else
                {
                    ppHierarchyNested = IntPtr.Zero;
                    pitemidNested = (uint)VSConstants.VSITEMID.Nil;
                    return VSConstants.E_NOINTERFACE;
                }
            }
            else
            {
                ppHierarchyNested = IntPtr.Zero;
                pitemidNested = 0;
                return VSConstants.DISP_E_MEMBERNOTFOUND;
            }
        }

        public int GetProperty(uint itemid, int propid, out object pvar)
        {
            LogGetPropertyAccess(itemid, propid);

            if (items.ContainsKey(itemid))
            {
                if (items[itemid].Properties.ContainsKey(propid))
                {
                    pvar = items[itemid].Properties[propid];
                    return VSConstants.S_OK;
                }
            }
            pvar = null;
            return VSConstants.DISP_E_MEMBERNOTFOUND;
        }

        private void LogGetPropertyAccess(uint itemid, int propid)
        {
            if (!bLogProperties) return;
            if (itemid == (uint)VSConstants.VSITEMID.Root && !bLogRootProperties) return;
            string sPropName;
            sPropName = GetPropName(propid);

            object propVal = "(No Value)";

            if (items.ContainsKey(itemid))
            {
                if (items[itemid].Properties.ContainsKey(propid))
                {
                    propVal = items[itemid].Properties[propid];
                }
            }

            System.Diagnostics.Debug.Print("Get Property for {0}: {1} = {2}", (VSConstants.VSITEMID)itemid, sPropName, propVal);
        }

        private void LogSetPropertyAccess(uint itemid, int propid, object value)
        {
            if (!bLogProperties) return;
            if (itemid == (uint)VSConstants.VSITEMID.Root && !bLogRootProperties) return;
            string sPropName = GetPropName(propid);
            System.Diagnostics.Debug.Print("Set Property for {0}: {1} = {2}", (VSConstants.VSITEMID)itemid, sPropName, value);
        }

        private static string GetPropName(int propid)
        {
            string sPropName;
            if (propid >= (int)__VSHPROPID.VSHPROPID_FIRST)
            {
                sPropName = ((__VSHPROPID)propid).ToString();
            }
            else if (propid >= (int)__VSHPROPID2.VSHPROPID_FIRST2)
            {
                sPropName = ((__VSHPROPID2)propid).ToString();
            }
            else if (propid >= (int)__VSHPROPID3.VSHPROPID_FIRST3)
            {
                sPropName = ((__VSHPROPID3)propid).ToString();
            }
            else
            {
                sPropName = ((__VSHPROPID4)propid).ToString();
            }
            return sPropName;
        }

        public int GetSite(out Microsoft.VisualStudio.OLE.Interop.IServiceProvider ppSP)
        {
            LogFunctionCall("GetSite");
            ppSP = site;
            return VSConstants.S_OK;
        }

        public int ParseCanonicalName(string pszName, out uint pitemid)
        {
            LogFunctionCall("ParseCanonicalName", "pszName", pszName);
            throw new NotImplementedException();
        }

        public int QueryClose(out int pfCanClose)
        {
            LogFunctionCall("QueryClose");
            pfCanClose = 1;
            return VSConstants.S_OK;
        }

        public int SetGuidProperty(uint itemid, int propid, ref Guid rguid)
        {
            return SetProperty(itemid, propid, rguid);
        }

        public int SetProperty(uint itemid, int propid, object var)
        {
            LogSetPropertyAccess(itemid, propid, var);
            if (items.ContainsKey(itemid))
            {
                items[itemid].Properties[propid] = var;
                return VSConstants.S_OK;
            }
            return VSConstants.DISP_E_MEMBERNOTFOUND;
        }

        public int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp)
        {
            LogFunctionCall("SetSite", "psp", psp);
            site = psp;
            return VSConstants.S_OK;
        }

        public int UnadviseHierarchyEvents(uint dwCookie)
        {
            LogFunctionCall("UnadviseHierarchyEvents", "dwCookie", dwCookie);
            eventSink = null;
            return VSConstants.S_OK;
        }

        public int Unused0()
        {
            LogFunctionCall("Unused0");
            throw new NotImplementedException();
        }

        public int Unused1()
        {
            LogFunctionCall("Unused1");
            throw new NotImplementedException();
        }

        public int Unused2()
        {
            LogFunctionCall("Unused2");
            throw new NotImplementedException();
        }

        public int Unused3()
        {
            LogFunctionCall("Unused3");
            throw new NotImplementedException();
        }

        public int Unused4()
        {
            LogFunctionCall("Unused4");
            throw new NotImplementedException();
        }

        #endregion

        #region IVsUIHierarchy Members
        void LogCommand(string preface, Guid cmdGroup, uint cmdId)
        {
            string sCmdName;
            IVsCmdNameMapping cmdNameMapper = (IVsCmdNameMapping)factory.Package.ServiceProvider.GetService(typeof(SVsCmdNameMapping));
            int hr = cmdNameMapper.MapGUIDIDToName(ref cmdGroup, cmdId, VSCMDNAMEOPTS.CNO_GETENU, out sCmdName);
            if (hr != VSConstants.S_OK)
            {
                sCmdName = String.Format("[{0}:{1}]", cmdGroup, cmdId);
            }
            System.Diagnostics.Debug.Print("{0}: {1}", preface, sCmdName);
        }

        public int ExecCommand(uint itemid, ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
        {
            if (bLogExecCommands) LogCommand(String.Format("IVsUiHierarchy.ExecCommand[{0}]", (VSConstants.VSITEMID)itemid), pguidCmdGroup, nCmdID);
            if (pguidCmdGroup == Microsoft.VisualStudio.Shell.VsMenus.guidVsUIHierarchyWindowCmds)
            {
                switch ((VSConstants.VsUIHierarchyWindowCmdIds)nCmdID)
                {
                    case VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_DoubleClick:
                    case VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_EnterKey:
                        OpenFile(itemid);
                        return VSConstants.S_OK;
                    case VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_RightClick:
                        ShowContextMenu(GetPointsFromVariant(pvaIn), GetCurrentlySelectedItem());
                        return VSConstants.S_OK;

                }

            } return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
        }

        private PovrayProjectNode GetCurrentlySelectedItem()
        {
            IVsMonitorSelection selMon = (IVsMonitorSelection)factory.Package.ServiceProvider.GetService(typeof(IVsMonitorSelection));

            IntPtr pHier;
            uint itemid;
            IVsMultiItemSelect multiSel;
            IntPtr pSC;

            selMon.GetCurrentSelection(out pHier, out itemid, out multiSel, out pSC);

            IVsHierarchy hier = (IVsHierarchy)Marshal.GetObjectForIUnknown(pHier);

            if (itemid == (uint)VSConstants.VSITEMID.Selection)
            {// Multi-select grab first item. 
                VSITEMSELECTION[] selItems = new VSITEMSELECTION[10];
                multiSel.GetSelectedItems(0, 10, selItems);

                return items[(uint)selItems[0].itemid].node;

            }
            else if(itemid != (uint)VSConstants.VSITEMID.Nil && itemid != 0)
            {
                return items[itemid].node;
            }
            return null; 
        }

        private POINTS GetPointsFromVariant(IntPtr pvaIn)
        {
            object obj = Marshal.GetObjectForNativeVariant(pvaIn);
            uint ppt = (uint)obj;

            short x = (short)(ppt & 0xFFFF);
            short y = (short)((ppt >> 16) & 0xFFFF);

            POINTS pts = new POINTS();
            pts.x = x;
            pts.y = y;

            return pts;
        }

        private void OpenFile(uint itemid)
        {
            if (items.ContainsKey(itemid))
            {
                string filename = items[itemid].Moniker;
                string caption = (string)items[itemid].Properties[(int)__VSHPROPID.VSHPROPID_Caption];
                IVsUIShellOpenDocument openDoc = (IVsUIShellOpenDocument)factory.Package.ServiceProvider.GetService(typeof(SVsUIShellOpenDocument));

                if (openDoc == null) return;
                IVsWindowFrame frame;
                Guid view = VSConstants.LOGVIEWID.TextView_guid;
                openDoc.OpenStandardEditor((uint)Microsoft.VisualStudio.Shell.Interop.__VSOSEFLAGS.OSE_ChooseBestStdEditor, filename, ref view, caption, this, itemid, IntPtr.Zero, site, out frame);
                frame.Show();

            }
        }

        private void ShowContextMenu(POINTS location, PovrayProjectNode target)
        {
            if (target != null)
            {
                IVsUIShell shell = (IVsUIShell)factory.Package.ServiceProvider.GetService(typeof(SVsUIShell));
                int mnu = VsMenus.IDM_VS_CTXT_ITEMNODE;

                if (Path.GetExtension(target.Filename) == ".povproj")
                {
                    mnu = VsMenus.IDM_VS_CTXT_PROJNODE;
                }

                shell.ShowContextMenu(0, VsMenus.guidSHLMainMenu,mnu, new POINTS[] { location }, target);
            }
          
        }

        public int QueryStatusCommand(uint itemid, ref Guid pguidCmdGroup, uint cCmds, Microsoft.VisualStudio.OLE.Interop.OLECMD[] prgCmds, IntPtr pCmdText)
        {
            if (bLogQueryComands) LogCommand(String.Format("IVsUiHierarchy.QueryCommand[{0}]", (VSConstants.VSITEMID)itemid), pguidCmdGroup, prgCmds[0].cmdID);
            IVsCmdNameMapping cmdNameMapper = (IVsCmdNameMapping)factory.Package.ServiceProvider.GetService(typeof(SVsCmdNameMapping));
            string sCmdName;
            int hr = cmdNameMapper.MapGUIDIDToName(pguidCmdGroup, prgCmds[0].cmdID, VSCMDNAMEOPTS.CNO_GETENU, out sCmdName);
            if (hr == VSConstants.S_OK)
            {
                Microsoft.VisualStudio.OLE.Interop.OLECMDTEXT cmdTxt = (Microsoft.VisualStudio.OLE.Interop.OLECMDTEXT)Marshal.PtrToStructure(pCmdText, typeof(Microsoft.VisualStudio.OLE.Interop.OLECMDTEXT));
                if (sCmdName == null) sCmdName = string.Empty;
                if (sCmdName.Length < (int)cmdTxt.cwBuf)
                {
                    cmdTxt.cwActual = (uint)sCmdName.Length;
                }
                else
                {
                    cmdTxt.cwActual = cmdTxt.cwBuf;
                }
                cmdTxt.rgwz = Marshal.StringToHGlobalUni(sCmdName);
                if (prgCmds[0].cmdf != 0) return VSConstants.S_OK;
                //Marshal.Copy(sCmdName.ToCharArray(), 0, cmdTxt.rgwz, (int)cmdTxt.cwActual);
            }
            return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
        }

        #endregion

        #region IPersistFileFormat Members

        public int GetClassID(out Guid pClassID)
        {
            LogFunctionCall("GetClassID");
            pClassID = GuidList.guidPkg_PovrayProjectFactory;
            return VSConstants.S_OK;
        }

        public int GetCurFile(out string ppszFilename, out uint pnFormatIndex)
        {
            LogFunctionCall("GetCurFile");
            if (String.IsNullOrEmpty(Filename))
            {
                ppszFilename = items[(uint)VSConstants.VSITEMID.Root].Moniker;
                pnFormatIndex = 0;
            }
            else
            {
                items[(uint)VSConstants.VSITEMID.Root].Moniker = Filename;
                items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_SaveName] = Filename;
                ppszFilename = Filename;
                pnFormatIndex = 0;
            }
            return VSConstants.S_OK;
        }

        public int GetFormatList(out string ppszFormatList)
        {
            LogFunctionCall("GetFormatList");
            ppszFormatList = string.Empty;
            return VSConstants.S_OK;
        }

        public int InitNew(uint nFormatIndex)
        {
            LogFunctionCall("InitNew", "nFormatIndex", nFormatIndex);
            return VSConstants.S_OK;
        }

        public int IsDirty(out int pfIsDirty)
        {
            LogFunctionCall("IsDirty");
            pfIsDirty = dirty ? 1 : 0;
            return VSConstants.S_OK;
        }

        XmlDocument currentDoc;

        private static Icon ProjectIcon = Resources.POV_Ray_Project;
        private static Icon PovFile = Resources.POV_Ray_Scene;
        private static Icon IncFile = Resources.POV_Ray_Include;

        public int Load(string pszFilename, uint grfMode, int fReadOnly)
        {
            LogFunctionCall("Load", "pszFilename", pszFilename, "grfMode", grfMode, "fReadOnly", fReadOnly);
            XmlDocument prjDoc = new XmlDocument();
            prjDoc.Load(pszFilename); // load the document from the template
            currentDoc = prjDoc;

            XmlNodeList nodes = prjDoc.SelectNodes("/project/files/*");
            string sourceDir = Path.GetDirectoryName(pszFilename);

            items.Add((uint)VSConstants.VSITEMID.Root, new ProjectItem() { Moniker = pszFilename, node = this });

            items[(uint)VSConstants.VSITEMID.Root].node = this;
            items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_Expandable] = 1;

            items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_OpenFolderIconHandle] = ProjectIcon.Handle;
            items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_IconHandle] = ProjectIcon.Handle;
            items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_StateIconIndex] = VsStateIcon.STATEICON_BLANK;
            items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_SaveName] = pszFilename;

            items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_BrowseObject] = Marshal.GetIDispatchForObject(new ProjectBrowseItem() { name = pszFilename });

            foreach (XmlNode item in nodes)
            {
                string filename = item.FirstChild.Value;
                string srcPath = Path.Combine(sourceDir, filename);
                if (new_item_id == 1)
                {
                    items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_FirstChild] = 1;
                    //items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_FirstVisibleChild] = 1;
                }
                uint id = new_item_id;
                items.Add(new_item_id++, new ProjectItem() { Moniker = srcPath, node = new PovrayProjectNode(factory) });
                items[id].Properties[(int)__VSHPROPID.VSHPROPID_Caption] = filename;
                object handle = GetProperHandle(filename);
                if (handle != null)
                {
                    items[id].Properties[(int)__VSHPROPID.VSHPROPID_IconHandle] = handle;
                }
                items[id].Properties[(int)__VSHPROPID.VSHPROPID_StateIconIndex] = VsStateIcon.STATEICON_BLANK;
                items[id].Properties[(int)__VSHPROPID.VSHPROPID_SaveName] = srcPath;
                if (id > 1)
                {
                    items[id - 1].Properties[(int)__VSHPROPID.VSHPROPID_NextSibling] = id;
                }

            }

            if (grfMode == 1) dirty = true;
            else dirty = false;
            return VSConstants.S_OK;
        }

        private object GetProperHandle(string filename)
        {
            if (Path.GetExtension(filename) == ".pov")
            {
                return PovFile.Handle;
            }
            if (Path.GetExtension(filename) == ".inc")
            {
                return IncFile.Handle;
            }
            return null;
        }

        public int Save(string pszFilename, int fRemember, uint nFormatIndex)
        {
            LogFunctionCall("Save", "pszFilename", pszFilename, "fRemember", fRemember, "nFormatIndex", nFormatIndex);
            currentDoc.Save(pszFilename);
            items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_ProjectName] = pszFilename;
            if (fRemember != 0)
            {
                items[(uint)VSConstants.VSITEMID.Root].Moniker = pszFilename;
                items[(uint)VSConstants.VSITEMID.Root].Properties[(int)__VSHPROPID.VSHPROPID_SaveName] = pszFilename;

                string destPath = Path.GetDirectoryName(pszFilename);

                foreach (var itm in items)
                {
                    if (itm.Key != (uint)VSConstants.VSITEMID.Root)
                    {
                        string srcFile = itm.Value.Moniker;
                        string dstFile = Path.Combine(destPath, Path.GetFileName(itm.Value.Moniker));
                        File.Copy(srcFile, dstFile);
                        itm.Value.Moniker = dstFile;
                        itm.Value.Properties[(int)__VSHPROPID.VSHPROPID_SaveName] = dstFile;
                    }
                }

                dirty = false;
            }

            return VSConstants.S_OK;
        }

        public int SaveCompleted(string pszFilename)
        {
            LogFunctionCall("SaveCompleted", "pszFilename", pszFilename);
            return VSConstants.S_OK;
        }

        #endregion

        public string Filename { get; set; }

        #region IOleCommandTarget Members

        public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
        {
             
            if (bLogExecCommands) LogCommand("IOleCommandTarget.Exec", pguidCmdGroup, nCmdID);

            if (pguidCmdGroup == Microsoft.VisualStudio.Shell.VsMenus.guidStandardCommandSet97)
            {
                switch ((VSConstants.VSStd97CmdID)nCmdID)
                {
                    case VSConstants.VSStd97CmdID.AddNewItem:
                        break;
                }
            }
          

            return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
        }

        public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, Microsoft.VisualStudio.OLE.Interop.OLECMD[] prgCmds, IntPtr pCmdText)
        {
            if (bLogQueryComands) LogCommand("IOleCommandTarget.QueryStatus", pguidCmdGroup, prgCmds[0].cmdID);

            IVsCmdNameMapping cmdNameMapper = (IVsCmdNameMapping)factory.Package.ServiceProvider.GetService(typeof(SVsCmdNameMapping));
            string sCmdName;
            int hr = cmdNameMapper.MapGUIDIDToName(pguidCmdGroup, prgCmds[0].cmdID, VSCMDNAMEOPTS.CNO_GETENU, out sCmdName);
            if (hr == VSConstants.S_OK)
            {
                prgCmds[0].cmdf = CheckCommandStatus(prgCmds[0].cmdID, pguidCmdGroup);

                if (prgCmds[0].cmdf != 0) LogCommand("IOleCommandTarget.QueryStatus", pguidCmdGroup, prgCmds[0].cmdID);

                Microsoft.VisualStudio.OLE.Interop.OLECMDTEXT cmdTxt = (Microsoft.VisualStudio.OLE.Interop.OLECMDTEXT)Marshal.PtrToStructure(pCmdText, typeof(Microsoft.VisualStudio.OLE.Interop.OLECMDTEXT));
                if (sCmdName == null) sCmdName = string.Empty;
                if (sCmdName.Length < (int)cmdTxt.cwBuf)
                {
                    cmdTxt.cwActual = (uint)sCmdName.Length;
                }
                else
                {
                    cmdTxt.cwActual = cmdTxt.cwBuf;
                }
                cmdTxt.rgwz = Marshal.StringToHGlobalUni(sCmdName);
                if (prgCmds[0].cmdf != 0) return VSConstants.S_OK;
                //Marshal.Copy(sCmdName.ToCharArray(), 0, cmdTxt.rgwz, (int)cmdTxt.cwActual);
            }
            return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
        }

        private uint CheckCommandStatus(uint p, Guid pguidCmdGroup)
        {
            uint ret = (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_SUPPORTED;
            if (pguidCmdGroup == Microsoft.VisualStudio.Shell.VsMenus.guidVsUIHierarchyWindowCmds)
            {

                switch ((VSConstants.VsUIHierarchyWindowCmdIds)p)
                {
                    case VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_CancelLabelEdit:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_CommitLabelEdit:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_DoubleClick:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_EnterKey:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_RightClick:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_StartLabelEdit:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    default:
                        return 0;
                }
            }
            else if (pguidCmdGroup == Microsoft.VisualStudio.Shell.VsMenus.guidStandardCommandSet97)
            {
                switch ((VSConstants.VSStd97CmdID)p)
                {
                    case VSConstants.VSStd97CmdID.AddExistingItem:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.AddNewItem:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.Copy:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.Cut:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.NewFolder:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.Open:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.OpenWith:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.Paste:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.Refresh:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.ReloadProject:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.Remove:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.Save:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.SaveAs:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.ShowExternals:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.Start:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.StartNoDebug:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.UnloadLoadedProject:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.UnloadProject:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    case VSConstants.VSStd97CmdID.ViewCode:
                        return ret | (uint)Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED;
                    default:
                        return 0;
                }
            }
            return 0;
        }

        #endregion

    }
}
